package com.appinit.lib;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @author GYL
 * @dateTime 2020/5/18-10:30
 * @description
 */
public class AppInitialManager {
  private static List<InitialItem> pathList;
  
  public static void addInitList(String initList) {
    if(pathList!=null && pathList.size()<1){
      return ;
    }
    try {
      Gson gson=new Gson();
      pathList =gson.fromJson(initList,new TypeToken<List<InitialItem>>(){}.getType());
      sortItem();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  
  private static boolean isReturn(final Context context){
    if (context == null) {
      return false;
    }
    if (pathList == null || pathList.size()<1) {
      return false;
    }
    return true;
  }
  
  private static void sortItem(){
    if (pathList !=null && pathList.size() > 1) {
      Collections.sort(pathList, new Comparator<InitialItem>() {
        @Override
        public int compare(InitialItem o1, InitialItem o2) {
          return o2.priority - o1.priority;
        }
      });
    }
  }
  
  public static void onInit(final Context context) {
    if(!isReturn(context)){
      return ;
    }
    HandlerThread handlerThread = new HandlerThread("initiator");
    handlerThread.start();
    Handler handler = new Handler(handlerThread.getLooper());
    for (InitialItem initItem : pathList) {
      final IAppInitial appInit = newClassInstance(initItem.classPath);
      if (appInit == null) {
        continue;
      }
      Runnable initRunnable = new Runnable() {
        @Override
        public void run() {
          long startTime = System.currentTimeMillis();
          appInit.onInit(context);
          long endTime = System.currentTimeMillis() - startTime;
          Log.d("AppInitialManager", "onInit [" + appInit.getClass().getSimpleName() + "- " + endTime + " (ms)]");
        }
      };
      if (!isAppDebug(context) && initItem.isOnlyInDebug) {
        continue;
      }
      if (!initItem.isInChildProcess && ThreadUtil.isChildProgress(context)) {
        continue;
      }
      long delay = initItem.delay;
      if (initItem.delay > 0) {
        if (initItem.isBackground) {
          handler.postDelayed(initRunnable, delay);
        } else {
          ThreadUtil.postDelayed(initRunnable, delay);
        }
      } else {
        if (initItem.isBackground) {
          handler.post(initRunnable);
        } else {
          ThreadUtil.postMain(initRunnable);
        }
      }
    }
  }
  
  public static void attachBaseContext(final Context context) {
    runStubMethod(context,"attachBaseContext",0);
  }
  
  public static void onTerminate(Context context){
    runStubMethod(context,"onTerminate",0);
  }
  
  private static void runStubMethod(Context context,String methodName,int level) {
    for (InitialItem initItem : pathList) {
      final IAppInitial appInit = newClassInstance(initItem.classPath);
      if (appInit == null) {
        continue;
      }
      if (!isAppDebug(context) && initItem.isOnlyInDebug) {
        continue;
      }
      long startTime = System.currentTimeMillis();
      if("attachBaseContext".equals(methodName)){
        appInit.attachBaseContext(context);
      }else if("onTerminate".equals(methodName)){
        appInit.onTerminate(context);
      }else if("onTrimMemory".equals(methodName)){
        appInit.onTrimMemory(level);
      }
      long endTime = System.currentTimeMillis() - startTime;
      Log.d("AppInitialManager", methodName+"[" + appInit.getClass().getSimpleName() + "- " + endTime + " (ms)]");
    }
  }
  
  public static void onTrimMemory(Context context,int level){
    runStubMethod(context,"onTrimMemory",level);
  }
  
  private static IAppInitial newClassInstance(String className) {
    IAppInitial result = null;
    try {
      result = (IAppInitial) Class.forName(className).getConstructor().newInstance();
    } catch (Throwable e) {
      e.printStackTrace();
    }
    return result;
  }
  
  private static boolean isAppDebug(Context context) {
    try {
      ApplicationInfo info = context.getApplicationInfo();
      return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
    } catch (Exception e) {
      Log.e("AppInitialManager", "isAppDebug: "+e.getMessage() );
    }
    return false;
  }
}
